/*! \file syscall.c 
 *	This files holds the implementations of all system calls.
 */

#include "kernel.h"
#include "sync.h"


	case SYSCALL_VERSION:
{
	SYSCALL_ARGUMENTS.rax = KERNEL_VERSION;
	break;
}

	case SYSCALL_PRINTS:
{
	kprints((char*) (SYSCALL_ARGUMENTS.rdi));
	SYSCALL_ARGUMENTS.rax = ALL_OK;
	break;
}

	case SYSCALL_PRINTHEX:
{
	kprinthex(SYSCALL_ARGUMENTS.rdi);
	SYSCALL_ARGUMENTS.rax = ALL_OK;
	break;
}

	case SYSCALL_PRINTAT:
{
	int row = SYSCALL_ARGUMENTS.rdi;
	int col = SYSCALL_ARGUMENTS.rsi;
	char *str = (char*)(SYSCALL_ARGUMENTS.rdx);

	if (kprintat(row, col, str)) {	// will mutate 
		SYSCALL_ARGUMENTS.rax = ALL_OK;
	} else {
		SYSCALL_ARGUMENTS.rax = ERROR;
	}

	break;
}
	
	case SYSCALL_DEBUGGER:
{
	/* Enable the bochs iodevice and force a return to the debugger. */
	outw(0x8a00, 0x8a00);
	outw(0x8a00, 0x8ae0);

	SYSCALL_ARGUMENTS.rax = ALL_OK;
	break;
}

/**
 *	The terminate system call terminates the currently running thread. The system call does 
 *	not take any parameters and will never return to the caller. It does thus not return any 
 *	values. Terminates the process when there are no threads left.
 */
	case SYSCALL_TERMINATE:
{
	/**
	 * TODO:
	 * 1. If killing the parent of some child processes, the parent_pid of the
	 * child processes should be set to the parent of the terminated process.
	 * 2. When killing a process the semaphore_table should be emptied of 
	 * semaphores belonging to that process.
	 */

	/* This will find the currently running thread, that we need to terminate.
	 * Also, the thread will be removed from the ready queue. */
	const int terminating_thread = cpu_private_data.thread_index;
	cpu_private_data.thread_index = -1;

	MENSURE(terminating_thread != -1, "Idle thread should never be terminated",{break;});

	int pid = thread_table[terminating_thread].data.owner; // The pid of the owner process of the thread.

	/* Terminate currently running thread */
	thread_table[terminating_thread].data.owner = -1; // The thread is now free to be allocated again.

	/* Terminate the calling process if needed */
	process_table[pid].threads--; // One thread of the process is removed


	/* If the process has no more running threads, clean it up */
	if (process_table[pid].threads == 0) {
		process_table[pid].parent = 0;
		long semaphore_handle;
		for (semaphore_handle = 0; semaphore_handle < MAX_NUMBER_OF_SEMAPHORES; semaphore_handle++) {
			if (semaphore_table[semaphore_handle].owner == pid) {
				semaphore_reset(semaphore_handle);
			}
		}
		cleanup_process(pid);
	}

	/* Find thread to execute; invoke the scheduler.
	 */
	schedule = 1; // Force a re-schedule

	break;
}



/**
 *	The createprocess system call takes a parameter in the rdi register . This parameter is an 
 *	integer which corresponds to an executable program. The program will be be started as a 
 *	process by the system call. The system call returns in the rax register a value of ALL_OK 
 *	if a process was successfully created and run. A value of ERROR is returned if any error 
 *	occurred which prevented a new process to be created or run.
 */
	case SYSCALL_CREATEPROCESS:
{
	//	TRACE("Entering SYSCALL_CREATEPROCESS");

	const long rdi = SYSCALL_ARGUMENTS.rdi; // The index of the program we want to start
	int new_pid = 0;
	struct prepare_process_return_value newproc;

	/* Do this when condition in MENSURE is false */
#define RETURN_ERROR	{SYSCALL_ARGUMENTS.rax = ERROR; break;}	

	/* Find first available PID. This must be when a process has no running threads */
	for (new_pid = 0; new_pid < MAX_NUMBER_OF_PROCESSES; new_pid++) { // linear search
		if (process_table[new_pid].threads == 0)
			break;
	}
	MENSURE(new_pid < MAX_NUMBER_OF_PROCESSES, "All PIDs occupied", RETURN_ERROR);

	/* Load program into memmory */
	newproc = prepare_process(executable_table[rdi].elf_image, new_pid, executable_table[rdi].memory_footprint_size);
	MENSURE(newproc.first_instruction_address != 0, "prepare_process failed", RETURN_ERROR);
	//	MENSURE(newproc.page_table_address != 0,		"prepare_process failed",  CREATEPROCESS_ERROR);	// XXX: When we implement VM

	/* Set the parent of the new process to be the calling process.
	 * XXX: When we (eventually) support multi-threading, increment .threads rather than setting to 1.
	 */
	const int current_thread = cpu_private_data.thread_index;
	MENSURE(current_thread < MAX_NUMBER_OF_THREADS, "Too many threads", RETURN_ERROR);
	const int parent_pid = thread_table[current_thread].data.owner; // Get PID of calling thread
	process_table[new_pid].parent = parent_pid; // We are the parent to the new process
	process_table[new_pid].threads = 1; // Make new process valid (by having non-zero threads)

	/* Finally, try to create the actual thread. Check if thread could be allocated. */
	int new_thread = allocate_thread();
	MENSURE(new_thread != -1, "Could not allocate thread", RETURN_ERROR);
	MENSURE(new_thread < MAX_NUMBER_OF_THREADS, "Should not happen. Error in allocate_thread", RETURN_ERROR);

	/* Store information into the new thread.
	 * Thread must start executing the program image.
	 */
	thread_table[new_thread].data.registers.integer_registers.rflags = 0x200; // set IF (interrupt flag); enable interrupts		http://en.wikipedia.org/wiki/IF_(x86_flag)
	thread_table[new_thread].data.owner = new_pid; // The new process owns the new thread.
	thread_table[new_thread].data.registers.integer_registers.rip = newproc.first_instruction_address;

	/* Assign the return value of the system call to rax in the callers context. */
	SYSCALL_ARGUMENTS.rax = ALL_OK; // We got this far, no MENSURE errors

	//	/* Switch to the new thread and let it execute. */
	//	current_thread = new_thread;	// Kernel will execute current_thread

	/* Enqeue the new thread for execution */
	thread_queue_enqueue(&ready_queue, new_thread);

	break;
}

	case SYSCALL_CREATETHREAD:
{
	const int rip = SYSCALL_ARGUMENTS.rdi;
	const int rsp = SYSCALL_ARGUMENTS.rsi;
	const int pid = thread_table[cpu_private_data.thread_index].data.owner;

	int new_thread = allocate_thread();
	MENSURE(new_thread != -1, "Could not allocate thread", RETURN_ERROR);
	MENSURE(new_thread < MAX_NUMBER_OF_THREADS, "Should not happen. Error in allocate_thread", RETURN_ERROR);

	process_table[pid].threads++;

	thread_table[new_thread].data.registers.integer_registers.rflags = 0x200; // set IF (interrupt flag); enable interrupts		http://en.wikipedia.org/wiki/IF_(x86_flag)
	thread_table[new_thread].data.owner = pid; // The new process owns the new thread.
	thread_table[new_thread].data.registers.integer_registers.rsp = rsp;
	thread_table[new_thread].data.registers.integer_registers.rip = rip;


	SYSCALL_ARGUMENTS.rax = ALL_OK; // We got this far, no MENSURE errors

	/* Enqeue the new thread for execution */
	thread_queue_enqueue(&ready_queue, new_thread);

	break;
}

	case SYSCALL_CREATESEMAPHORE:
{
	const int cnt = SYSCALL_ARGUMENTS.rdi;

	long semaphore_handle;
	// We start by finding a free slot in the semaphore_table
	for (semaphore_handle = 0; semaphore_handle < sizeof (semaphore_table) / sizeof (semaphore_table[0]); semaphore_handle++) {
		if (semaphore_table[semaphore_handle].owner == -1)
			goto found;
	}

	SYSCALL_ARGUMENTS.rax = ERROR;
	break;

found:
	// The owner and the count of the new semaphore are set.
	semaphore_table[semaphore_handle].owner = thread_table[cpu_private_data.thread_index].data.owner;
	semaphore_table[semaphore_handle].count = cnt;


	SYSCALL_ARGUMENTS.rax = semaphore_handle;

	break;
}

/**
 * The System call SYSCALL_SEMAPHOREDOWN decrements the semaphore if the
 * semaphore is greater than zero, else it blocks. The systemcall returns
 * an error if the parent process of the calling thread is not the owner
 * of the semaphore.
 */
	case SYSCALL_SEMAPHOREDOWN:
{
	//TRACE("SEMAPHOREDOWN is called");
	const long semaphore_handle = SYSCALL_ARGUMENTS.rdi;
	const int pid = thread_table[cpu_private_data.thread_index].data.owner;
	// Proceed if the parent process of the calling thread owns the semaphore.
	if (semaphore_table[semaphore_handle].owner == pid) {
		// If the value of the semaphore is Zero the calling thread should be blocked.
		if (semaphore_table[semaphore_handle].count == 0) {
			semaphore_enqueue(cpu_private_data.thread_index, semaphore_handle);
			//DEBUG(cpu_private_data.thread_index);
			//DEBUG(semaphore_handle);
			/* 
			 * If the  semaphore is Zero the calling thread should be blocked
			 * and saved onto some data structure that is checked if a semaphore
			 * released.
			 * and the scehduler should run.
			 */
			//SYSCALL_ARGUMENTS.rax = ERROR;
			SYSCALL_ARGUMENTS.rax = ALL_OK;
			schedule = 1;
		} else {
			// If the value of the semaphore is not Zero decrement the value of the semaphore.
			semaphore_table[semaphore_handle].count--;
			SYSCALL_ARGUMENTS.rax = ALL_OK;
		}
	} else {
		SYSCALL_ARGUMENTS.rax = ERROR;
	}

	break;
}

/**
 * SYSCALL_SEMAPHOREUP increments the given semaphore if no blocked
 * threads are waiting for the semaphore.The systemcall first checks if the
 * parent process of the calling thread is the owner of the semaphore.
 */
	case SYSCALL_SEMAPHOREUP:
{
	//TRACE("SEMAPHOREUP is called");
	const long semaphore_handle = SYSCALL_ARGUMENTS.rdi;
	const int pid = thread_table[cpu_private_data.thread_index].data.owner;

	if (semaphore_table[semaphore_handle].owner == pid) {
		if (semaphore_table[semaphore_handle].count == 0) {
			const int head = semaphore_table[semaphore_handle].blocked.head;
			const int tail = semaphore_table[semaphore_handle].blocked.tail;
			if (head != tail) {
				const int thread_index = semaphore_dequeue(semaphore_handle);
				thread_queue_enqueue(&ready_queue, thread_index);
			} else {
				semaphore_table[semaphore_handle].count++;
			}
			// There might be threads blocked for this semaphore
			//semaphore_table[semaphore_handle].blocked.
		} else {
			semaphore_table[semaphore_handle].count++;
		}
		SYSCALL_ARGUMENTS.rax = ALL_OK;
	} else {
		SYSCALL_ARGUMENTS.rax = ERROR;
	}
	break;
}

